# -*- coding: utf-8 -*-
# @Time    : 2024/6/17 14:21
# @Author  : yujiahao
# @File    : 26_object.py
# @description:Python中的面向对象_1

"""
【先了解几个概念】
    - 面向对象的程序设计的核心是对象（上帝式思维），要理解对象为何物，必须把自己当成上帝，上帝眼里世间存在的万物皆为对象，不存在的也可以创造出来。
    - 面向对象是相对于面向过程而言的。面向过程强调的是功能行为。面向对象，将功能封装进对象，强调具备了功能的对象。
    - 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则，如抽象、分类、继承、聚合、多态等。
    - 面向对象的三大特征:封装  (Encapsulation)、继承  (Inheritance)、多态  (Polymorphism)

问题来了,那什么是类?什么又是对象?

    类：就是具有相同属性和功能的一类事物
    对象：类的实体，也称为实例(instance)，就是类的具体表现形式



    具体一些：先解释解释什么是⻋? 有轱辘, 有⽅向盘, 有发动机, 会跑的是⻋. 好. 在解释⼀个. 什么是⼈. 有名字, 年龄, 爱好, 会唱歌跳舞思考的是⼈.那么广义上 车，人就是类：但是具体的我的车，你这个人这是一个对象。
    猫，是一类，你们家养的 大橘。
    狗，是一类，隔壁家养的那只二哈就是对象。
    ⾯向对象思维, 要⾃⼰建立对象. ⾃⼰建立场景. 你是就是⾯向对象世界中的上帝. 你想让⻋⼲嘛就⼲嘛. 你想让⼈⼲嘛⼈就能⼲嘛。

    【第一个优点】

        面向对象编程：是一类相似功能函数的集合,使你的代码更清晰化，更合理化。

    【第二个优点】面向对象，要拥有上帝的视角看问题，类其实就是一个公共模板（厂房），对象就从具体的模板实例化出来（慢慢体会）。




Python

    对象的创建：使用类名创建一个新的对象，例如 person = Person("张三", 25)

    匿名对象：我们也可以不定义对象的句柄，而直接调用这个对象的方法。这样的对象叫做匿名对象。
    如：Person("李四", 30).shout()

    匿名对象使用情况：
    1. 如果对一个对象只需要进行一次方法调用，那么就可以使用匿名对象。
    2. 我们经常将匿名对象作为实参传递给一个方法调用。

    属性和方法的调用:
    1. 使用“对象名.对象成员”的方式访问对象成员（包括属性和方法）；
    2. self表示对象, self.name表示对象的name属性。

    属性初始化：
    1. 隐式初始化：Python中没有隐式初始化，所有实例变量必须在构造方法中显式初始化。
    2. 显示初始化：如 name = "张三"。

    类中用属性描述的特征，数据，成员变量。
    类中用方法描述事物的行为，动作，功能。

    方法：方法描述行为动作
    1. 成员方法 也称为对象方法或实例方法
    2. 表达式：def 方法名(self, 形参列表): 方法体

    成员变量：
    * 成员变量定义在类中，在整个类中都可以被访问。
    * 成员变量分为类成员变量和实例成员变量。
    * 实例变量存在于对象所在的内存中。
    * 成员变量没有默认初始化值，必须显式初始化。

    局部变量：
    * 局部变量只定义在局部范围内，如：方法内，代码块内等。
    * 局部变量存在于栈内存中。
    * 作用的范围结束，变量空间会自动释放。
    * 局部变量没有默认初始化值，每次必须显式初始化。
    * 局部变量声明时不指定权限修饰符。

    成员都可以使用访问控制修饰符来修饰public private
    关键字self使用：
    self表示当前对象, self.name表示对象的name属性。
    self.xxx, self.yyy() 访问对象的成员, 包括属性和方法。

    封装：
    1. Python中用单下划线或双下划线前缀来表示“私有”成员，但这只是约定俗成的，并不是真正的访问控制。
    2. 成员私有化，其他类只能通过方法间接访问，就可以在方法中对属性进行一个保护。
    3. set方法用于间接给属性赋值, 有参无返回；get方法无参有返回。
    4. 方法的参数名最好详细且有意义, 当参数和属性名冲突时, 要特别加上self限定, 用于标识对象属性。

    构造器(constructor)：也称为构造方法，作用是对象创建时进行初始化工作。

    语法格式：
    def __init__(self, 参数列表):
        初始化语句；

    举例：
    class Animal:
        def __init__(self):
            self.legs = 4  # 构造器
        def set_legs(self, i):
            self.legs = i
        def get_legs(self):
            return self.legs

    特点：
    1. 构造器名为__init__，首字母小写。
    2. 没有返回值声明，甚至连None都没有。
    3. 不能被静态方法修饰，不能有return语句返回值。
    4. 不能像普通方法被随意调用，同一个对象的生命周期内，只能被调用一次。
    5. 如果在类中没有写构造器，Python不会自动添加一个缺省构造器。

    说明：
    1. 如果在类中写了构造器，Python就不会自动添加任何构造器。
    2. 同一个类中如果参数不同构造器可以构成重载。
    3. 构造器会在创建对象时最后赋值，显示赋值在前。
    4. Python不支持方法重载，因此不能像Java那样定义多个构造器。但可以通过默认参数和可变参数来实现类似的效果。


Java

    对象的创建：1>使用new +构造器创建一个新的对象；类名 对象名 = new 类名(); 例如 MyDate i = new MyDate();

    匿名对象：1> 我们也可以不定义对象的句柄，而直接调用这个对象的方法。这样的对象叫做匿名对象。
            如：new Person().shout();

    匿名对象使用情况：
            1> 如果对一个对象只需要进行一次方法调用，那么就可以使用匿名对象。
            2> 我们经常将匿名对象作为实参传递给一个方法调用。


    属性和方法的调取: 1> 使用“对象名.对象成员”的方式访问对象成员（包括属性和方法）；
                    2> this表示对象, this.name表示对象的name属性.
    属性初始化：
                1> 隐式初始化都是0，虚拟机帮我们完成的具体对应的是相应的值，String:0 => null
                2> 显示初始化：String name = "张三";显示初始化
        类中用属性描述的特征，数据，成员变量
        类中用方法描述事物的行为，动作，功能
     方法：方法描述行为动作,
            1> 成员方法 也称为对象方法或实例方法
            2> 表达式：修饰符 返回值类型 方法名(形参列表) {方法体}
    成员变量：
         * 成员变量定义在类中，在整个类中都可以被访问。
         * 成员变量分为类成员变量和实例成员变量，
         * 实例变量存在于对象所在的堆内存中。
         * 成员变量有默认初始化值。
         * 成员变量的权限修饰符可以根据需要，选择任意一个

    局部变量：
         * 局部变量只定义在局部范围内，如：方法内，代码块内等。
         * 局部变量存在于栈内存中。
         * 作用的范围结束，变量空间会自动释放。
         * 局部变量没有默认初始化值，每次必须显式初始化。
         * 局部变量声明时不指定权限修饰符
    成员都可以使用访问控制修饰符来修饰public private
    关键字this使用：this.表示对象，
           this 表示当前对象, 2种用法
        1) this.xxx, this.yyy() 访问对象的成员, 包括属性和方法
        2) this(...) 连环调用其他构造器
            注意：
                1.使用this()必须放在构造器的首行！
                2.使用this调用本类中其他的构造器，保证至少有一个构造器是不用this的。
                3. 如果在构造器中, this代表正在创建的对象
               4.如果在普通方法中, this表示此方法的调用者对象.

          //name = name; // 左面的name因为就近原则, 定位的是参数name, 而不是属性name 因此不对的要加this
            this.name = name; // this表示对象, this.name表示对象的name属性.


    封装：   ① private修饰成员，表示是私有的，只能在本类中使用，可以被其他成员访问,
            ②成员私有化，其他类只能通过方法间接访问，就可以在方法中对属性进行一个保护，private保护对象
            ③this.表示对象
            ④set方法用于间接给属性赋值, 有参无返回,get方法无参有返回
             ⑤方法的参数名最好详细且有意义, 当参数和属性名冲突时, 要特别加上this限定, 用于标识对象属性
    构造器(constructor)：也称为构造方法，作用是对象创建时进行初始化工作；

        1>语法格式：
                修饰符  类名 (参数列表) {
                        初始化语句；
                }
        2>举 例：
                public class Animal {
                private int legs;
                public Animal() {legs = 4; }	   //构造器
                public void setLegs(int i) { legs = i; }
                public int getLegs(){return legs;}
                }

    特点：   ①构造器名与类名相同，首字母大写，唯一一个允许首字母大写的方法
            ②没有返回值声明，甚至连void都没有
            ③不能被Static final synchronized abstract native修饰，不能有return语句返回值
            ④不能像普通方法被随意调用，同一个对象的生命周期内，只能被调用一次
            ⑤如果在类中没有写构造器，编译器会自动添加一个缺省构造器
    缺省构造器：
                 1>修饰符和类一致
                 2>无参
                 3>无语句
    说明：    ①如果在类中写了构造器，编译器就不在添加任何构造器
             ②同一个类中如果参数不同构造器可以构成重载
             ③构造器方法的名称在编译时会转换为<init>
             ④构造器会在创建对象时最后赋值，显示赋值在前
     * this(...) 必须放在第一行, 效果是连环调用先于本构造器的执行. 父类构造器先于子类构造器.
     * this(...) 一定要有一个构造器中是没有this(...)的. 否则会形成无限递归


Scala

     TODO 面向对象 -- 类
    具有相同的特征和行为，对特征和行为进行抽象所形成的语法结构，称之为【类】
      java中的类
          使用class关键字声明类，只声明自身、【java】中的【一个源码文件中可以声明多个类】，【但是只能声明一个公共类】
            【且名字和源码文件名相同】。
      Scala中的一个源码文件中可以声明多个类，且都是公共的，Scala 中的类也是【单继承】
        使用类构件对象的时，可以采用new的方式

  TODO 类的属性

     1、 Scala中的属性其实就是类中声明的变量
        静态属性：通过类名.属性直接访问
        成员属性：通过对象.属性直接访问
      Scala中的方法其实就是类中声明的函数
    2、属性和变量有区别：
      作用域：属性从属于对象，和对象的范围有关系
      访问权限：属性有访问权限，但是变量没有
    3、属性初始化和变量是否一样：
    ①【变量】必须声明和初始化放在一起，【属性】也必须要放在一起
    ②java在构件对象是会给属性进行默认初始化，所以声明时不初始化是可以的，Scala也想实现这样的功能怎么办？
      >> 需要采用【特殊符号】：_
          注意：下划线进行默认初始化的操作只对var声明的属性起作用，一般情况下，类中数属性的声明时，
            都采用var ,不采用val,但是在逻辑代码中声明的变量都采用val
    4、Scala类中的属性在编译时会让属性private私有的
        >>【私有的访问权限】：只有类的内部可以使用
    5、Scala属性本质：
      1、编译时，类的属性会被编译为私有的成员属性
      2、编译时会同时生成属性对应的set（赋值）和get（取值）方法
      3、访问属性时，其实对应访问属性的方法
        给属性赋值，等同于访问属性的set方法
        取属性的值，等同于访问属性的get方法
      4、为了和java的bean规范保持统一，可以采用特殊的注解：@BeanProperty
        增加注解后会自动生成bean规范的set get 方法，这里的是给属性前面加注解。
              @BeanProperty var age: Int = 24
              user.setAge(20)
      5、如果属性使用val声明，那么编译时，生成的属性是final的,且只会生成对应的get方法，没有set方法
      6、属性可以使用访问权限,但是属性对应的set 和get方法也是私有的

  TODO 面向对象--访问权限

    1、java的访问权限：
      1、private：同类中使用
      2、缺省（default）：同类，同包
      3、protected：同类，同包，子类
      4、public：任何地方
    2、Scala访问权限：也有四种
      1、private：私有的--同类中使用
      2、private [包名]：包私有--同类、同包、子包可以使用
      3、protected：同类，子类
      4、（default）：公共的
    3、什么叫访问权限？：访问的权利和限制
      所谓的权限，其实就是一种关系，程序中的权限也是一回事，以方法调用为例，方法的提供者和方法调用者之间的关系。
      1、子类能不能继承父类私有的属性？
        【可以继承】但是【无法<<直接>>】使用
        【继承】表示的是【有没有】，【访问权限】表示怕【能不能用】
      2、声明一个类，这个类中有没有方法？
        java中所有的类都继承了Object类中的方法--自己重写之后就可以调用了
        clone方法能不能使用：取决于clone方法的提供者和调用者的关系
        clone方法的提供者是：java.long.Object
        clone方法的调用者是：例子当中包下面的main线程所在的类
        java中的点的作用：这里的点不是调用的意思，表示从属关系，可以表示【的】

    TODO eq 在编译之后其实就是双等号，用于比较对象的内存地址

        平时使用时都是采用类似于 运算符的方式
          对象.eq(true)
          对象 eq true
      java 中equals判断原则：
        1、类型相同或有关系
        2、判断属性或特征是否相同
        instanceof
        Scala中重写对象的equals方法
        用方法isIntanceOf

    TODO 面向对象--类-方法

     类中声明的函数就是方法，遵循方法的限制和约束，方法也有访问权限。
      方法是否能够使用，取决于类型而不是对象，方法谁提供，看类型，方法如何运行看到对象。
    TODO 父类和子类都具有相同的方法时，如何选择？

     双亲委派机制：JDK中的类和你的类冲突了，使用谁的？
     动态绑定机制：运行时JVM调用（直接和间接）对象的成员方法时，会将方法和实际的内存对象做绑定，然后调用
     注意：属性不存在动态绑定机制，在哪里声明，在哪里使用

        TODO 面向对象 - 对象
    所谓对象，其实就是根据类的信息，创建的独立的个体
    1、new 可以创建对象
    2、反射:方法区内存-框架中用的多
    3、clone：用的不多：native
    4、反序列化：网络中传输数据 --字节码
    Scala中可以直接使用Object关键字声明对象
      导入对象（使用object声明的对象，类似于静态导入）
          import Object._
           test1()
           test2()
           test3()
      导入对象 （new 创建的对象）--方式一这里的类型必须是val 不是var
    val hh :user = new user()
      导入对象 （new 创建的对象）--方式二:对象名._
        import hh._
        test4()
  TODO 面向对象- object关键字

    ①Scala中有一个特殊的关键字【object关键字】，用于声明和创建对象，这个对象是单例，【主要用于模拟静态语法】
    ②Scala中object类中声明的main方法是成员方法，是单例对象的成员方法
    ③Object关键字可以声明类，其中main方法在底层编译为静态入口方法main，静态main方法中会调用当前object类中的main方法
    ④object关键字声明类的同时，也声明对象，对象的名字就是类名，且类型为当前类型名+$，因为object关键字是可以声明类和对象的，
        所以有时候会认为单例对象是伴随着类产生的对象，
        所以这个单例也称之为：【伴生对象】
        所以这个类也称之为：【伴生类】
        可以单独声明伴生类，也可以同时声明伴生类和伴生对象

   TODO 如何区分和使用伴生类和伴生对象

      伴生对象的目的就是为了模拟静态语法
      将类的成员方法和成员属性声明在伴生类中
      将类的静态方法和静态属性声明在伴生对象中

总结来说，Scala 在对象创建、属性和方法的调用、属性初始化、方法定义、成员变量和局部变量的使用、封装以及构造器方面都提供了简洁而灵活的语法。通过使用 `new` 关键字和构造器来创建对象，匿名对象可以直接调用方法。属性和方法的调用通过对象名加上对象成员的方式进行，`this` 关键字表示当前对象。属性可以隐式初始化为默认值，也可以显式赋值。方法定义简洁直观，成员变量和局部变量的定义和初始化也很直观。封装通过 `private` 修饰符和 `getter`、`setter` 方法实现。构造器分为主构造器和辅助构造器，主构造器直接跟在类名后面，辅助构造器使用 `this` 关键字定义。Scala 的这些特性使得代码更加简洁和易于维护。







类的结构

    类定义
        Java: 使用 class 关键字定义类。类名后面不需要括号，类体用 {} 包围。
        Scala: 使用 class 关键字定义类。类名后面可以直接跟主构造器参数列表，类体用 {} 包围。
        Python: 使用 class 关键字定义类。类名后面不需要括号，类体用缩进表示。
    属性
        Java: 属性通常定义为私有 (private)，通过 getter 和 setter 方法访问。
        Scala: 属性可以直接定义为 var 或 val，并且可以直接访问。
        Python: 属性直接定义在构造方法 __init__ 中，可以直接访问。
    构造器
        Java: 使用与类名相同的方法名定义构造器。构造器可以重载以实现不同的初始化方式。
        Scala: 主构造器直接跟在类名后面。辅助构造器使用 this 关键字定义。
        Python: 使用 __init__ 方法定义构造器。
    方法
        Java: 方法定义在类体内，方法名后面跟参数列表和方法体。可以定义为 public、private 等。
        Scala: 方法定义在类体内，使用 def 关键字。方法名后面跟参数列表和方法体。
        Python: 方法定义在类体内，使用 def 关键字。方法名后面跟参数列表和方法体。
    主方法
        Java: 使用 public static void main(String[] args) 作为程序入口点。
        Scala: 使用 object 定义单例对象，并继承 App 特质，或者定义 main 方法。
        Python: 使用 if __name__ == "__main__": 作为程序入口点。
    类的命名
        Java: 类名通常使用大驼峰命名法（PascalCase），如 Person。
        Scala: 类名通常使用大驼峰命名法（PascalCase），如 Person。
        Python: 类名通常使用大驼峰命名法（PascalCase），如 Person。

    注意事项
    Java:

        类文件名必须与类名相同，并且区分大小写。
        每个类文件只能有一个 public 类。
        必须显式声明数据类型。
    Scala:

        支持函数式编程和面向对象编程的混合风格。
        可以省略一些冗余代码，如分号和类型声明。
        支持不可变数据结构。
    Python:

        动态类型语言，属性和方法可以在运行时动态添加。
        强调代码的可读性，使用缩进表示代码块。
        内置了许多高级数据结构和库，适合快速开发。

    通过以上对比，可以看到每种语言在类的定义和使用上都有各自的特点。Java较为冗长但明确，Scala简洁且灵活，Python则直观且易读。
"""

# todo 一.面向对象初识

# 1.1 面向过程编程vs函数式编程

'''
我们在没有学习函数的时候,写的代码都是面向过程式编程
通过对比可知：函数编程较之面向过程编程最明显的两个特点：
    1，减少重复的代码。
    2，增强代码的可读性。
'''

# 面向过程编程 测量对象的元素的个数。
s1 = 'fjdsklafsjda'
count = 0
for i in s1:
    count += 1

l1 = [1, 2, 3, 4]
count = 0
for i in l1:
    count += 1


# 在我们学习函数后就是在面向函数编程

def func(s):
    count = 0
    for i in s:
        count += 1
    return count


func('fdsafdsa')
func([1, 2, 3, 4])

# 1.2、函数式编程vs面向对象编程


'''

面向对象编程：是一类相似功能函数的集合,使你的代码更清晰化，更合理化。

下面两段代码：
   
    函数式编程
    
    
            # auth 认证相关
            def login():
                pass
            
            def regisgter():
                pass
            
            # account 账户相关
            def func1():
                pass
            
            def func2():
                pass
            
            
            # 购物车相关
            def shopping(username,money):
                pass
            def check_paidgoods(username,money):
                pass
            def check_unpaidgoods(username,money):
                pass
            def save(username,money):
        pass
        
    面向对象编程
    
            class LoginHandler:
                def login(self):
                    pass
            
                def regisgter(self):
                    pass
            
            class Account:
                def func1(self):
                    pass
            
                def func2(self):
                    pass
            
            class ShoppingCar:
                def shopping(username,money):
                    pass
                def check_paidgoods(username,money):
                    pass
                def check_unpaidgoods(username,money):
                    pass
                def save(username,money):
                    pass
                                     
                
通过对比可以看出面向对象的优点：
    第一个优点：面向对象编程：是一类相似功能函数的集合,使你的代码更清晰化，更合理化。
    第二个优点：面向对象，要拥有上帝的视角看问题，类其实就是一个公共模板（厂房），对象就从具体的模板实例化出来
'''

# todo 二、类


'''
class 是关键字与def用法相同，定义一个类。

class 类名 ：【冒号】

Human是此类的类名，类名使用驼峰(CamelCase)命名风格，首字母大写，私有类可用一个下划线开头。
类的结构从大方向来说就分为两部分：
静态变量。
动态方法。


Python：
使用 class 关键字定义类。
类中可以包含属性（变量）和方法（函数）。
类的属性可以是静态属性。
Java：
使用 class 关键字定义类。
类中可以包含静态属性（static 变量）和方法（static 方法）。
Scala：Scala中一般使用伴生对象Object定义
使用 class 关键字定义类。
类中可以包含属性和方法。
属性可以有默认值，如 var mind: String = "有思想"
        
'''

# 2.1 类的成员


"""
    A 类成员的详细概念解释：

    1. 静态变量（静态字段）

    静态变量是类级别的变量，所有实例共享。可以通过类名或实例来访问和修改静态变量。

    - company_name：公开的静态变量，所有实例共享。
      - 用途：存储与类相关的公共信息。
      - 访问方式：通过类名或实例访问。
      - 示例：
        ```python
        print(A.company_name)  # 通过类名访问
        instance = A("Alice", 30)
        print(instance.company_name)  # 通过实例访问
        A.company_name = "新公司名"
        print(instance.company_name)  # 输出: 新公司名
        ```

    - __iphone：私有静态变量，仅限于类内部访问，不对外部公开。
      - 用途：存储类内部的私有信息。
      - 访问方式：只能在类内部访问。
      - 示例：
        ```python
        # 在类内部访问
        print(A._A__iphone)  # 通过名称改写规则访问（不推荐）
        ```

    2. 特殊方法

    特殊方法是由双下划线包围的方法，通常在类的生命周期中自动调用。

    - __init__(self, name, age)：构造方法，用于初始化对象的实例属性。每次创建类的实例时，都会调用这个方法。
      - 用途：初始化实例属性。
      - 访问方式：自动调用，无需手动调用。
      - 示例：
        ```python
        instance = A("Alice", 30)
        print(instance.name)  # 输出: Alice
        ```

    3. 对象属性（普通字段）

    对象属性是实例级别的变量，每个实例都有自己独立的值。

    - name：公开的实例属性，可以通过实例访问和修改。
      - 用途：存储实例的公开信息。
      - 访问方式：通过实例访问和修改。
      - 示例：
        ```python
        instance = A("Alice", 30)
        print(instance.name)  # 输出: Alice
        instance.name = "Bob"
        print(instance.name)  # 输出: Bob
        ```

    - __age：私有实例属性，仅限于类内部访问，不对外部公开。
      - 用途：存储实例的私有信息。
      - 访问方式：只能在类内部访问。
      - 示例：
        ```python
        instance = A("Alice", 30)
        # 只能在类内部访问
        print(instance._A__age)  # 通过名称改写规则访问（不推荐）
        ```

    4. 普通方法

    普通方法是定义在类中的方法，必须通过实例调用。

    - func1(self)：普通方法，可以通过实例调用。
      - 用途：定义实例行为。
      - 访问方式：通过实例调用。
      - 示例：
        ```python
        instance = A("Alice", 30)
        instance.func1()
        ```

    5. 私有方法

    私有方法前面有双下划线，表示它是类内部使用的。

    - __func(self)：私有方法，仅限于类内部调用。
      - 用途：定义类内部的私有行为。
      - 访问方式：只能在类内部调用。
      - 示例：
        ```python
        # 在类内部调用
        instance._A__func()  # 通过名称改写规则访问（不推荐）
        ```

    6. 类方法

    类方法使用 @classmethod 装饰，第一个参数通常命名为 cls，表示类本身。可以通过类名或实例调用。

    - class_func(cls)：类方法，可以通过类名或实例调用。
      - 用途：定义类级别的行为，通常用于操作类属性。
      - 访问方式：通过类名或实例调用。
      - 示例：
        ```python
        A.class_func()  # 通过类名调用
        instance = A("Alice", 30)
        instance.class_func()  # 通过实例调用
        ```

    7. 静态方法

    静态方法使用 @staticmethod 装饰，没有默认参数。可以通过类名或实例调用。

    - static_func()：静态方法，可以通过类名或实例调用。
      - 用途：定义与类和实例无关的行为，通常是一些工具方法。
      - 访问方式：通过类名或实例调用。
      - 示例：
        ```python
        A.static_func()  # 通过类名调用
        instance = A("Alice", 30)
        instance.static_func()  # 通过实例调用
        ```

    8. 属性

    属性使用 @property 装饰，可以像访问属性一样访问它。

    - prop：属性，可以像访问属性一样访问。
      - 用途：提供对实例属性的访问控制。
      - 访问方式：通过实例访问，像访问属性一样。
      - 示例：
        ```python
        instance = A("Alice", 30)
        print(instance.prop)  # 像访问属性一样访问
        ```
"""


class A:
    company_name = '我是静态变量'  # 静态变量(静态字段)
    __iphone = '我是__开头是私有静态变量'  # 私有静态变量(私有静态字段)

    def __init__(self, name, age):  # 特殊方法
        self.name = name  # 对象属性(普通字段)
        self.__age = age  # 私有对象属性(私有普通字段)

    def func1(self):  # 普通方法
        pass

    def __func(self):  # 私有方法
        print(666)

    @classmethod  # 类方法
    def class_func(cls):
        """ 定义类方法，至少有一个cls参数 """
        print('类方法')

    @staticmethod  # 静态方法
    def static_func():
        """ 定义静态方法，无默认参数 """
        print('静态方法')

    @property  # 属性
    def prop(self):
        pass


# 2.2 类的私有成员

'''
对于每一个类的成员而言都有两种形式：

    - 公有成员，在任何地方都能访问
    - 私有成员，只有在类的内部才能方法
    - 私有成员和公有成员的访问限制不同：

静态字段(静态属性)

    - 公有静态字段：类可以访问；类内部可以访问；派生类中可以访问
    - 私有静态字段：仅类内部可以访问；
    
总结:

    对于这些私有成员来说,他们只能在类的内部使用,不能再类的外部以及派生类中使用.
    ps：非要访问私有成员的话，可以通过 对象.类_属性名,但是绝对不允许!!!
    为什么可以通过.类__私有成员名访问呢?因为类在创建时,如果遇到了私有成员(包括私有静态字段,私有普通字段,私有方法)它会将其保存在内存时自动在前面加上类名.
'''


# 2.2.1 公有静态属性(字段)
class C:
    name = "公有静态字段"

    def func(self):
        print(C.name)


class D(C):

    def show(self):
        print(C.name)


C.name  # 类访问

obj = C()
obj.func()  # 类内部可以访问

obj_son = D()
obj_son.show()  # 派生类中可以访问


# 2.2.2 私有静态属性(字段)
class C:
    __name = "私有静态字段"

    def func(self):
        print(C.__name)


class D(C):

    def show(self):
        print(C.__name)


# C.__name  # 不可在外部访问

obj = C()
# obj.__name  # 不可在外部访问
obj.func()  # 类内部可以访问

obj_son = D()
# obj_son.show()  # 不可在派生类中可以访问

'''
普通字段(对象属性)

    公有普通字段：对象可以访问；类内部可以访问；派生类中可以访问
    私有普通字段：仅类内部可以访问；
'''



# 2.2.3 公有普通字段
class C:

    def __init__(self):
        self.foo = "公有字段"

    def func(self):
        # 　类内部访问
        print (self.foo)

class D(C):

    def show(self):
        # 派生类中访问
        print (self.foo)

obj = C()

obj.foo     # 通过对象访问
obj.func()  # 类内部访问

obj_son = D();
obj_son.show()  # 派生类中访问





# 2.2.4 私有对象属性
class C:

    def __init__(self):
        self.__foo = "私有字段"

    def func(self):
        # 类内部访问
        print (self.__foo)

class D(C):

    def show(self):
        # 　  派生类中访问
        print (self.__foo)

obj = C()

# obj.__foo     # 通过对象访问    ==> 错误
obj.func()  # 类内部访问        ==> 正确

obj_son = D()
# obj_son.show()  # 派生类中访问  ==> 错误








'''
方法:
公有方法:对象可以访问；类内部可以访问；派生类中可以访问 
私有方法:仅类内部可以访问；'''

# 2.2.5 公有方法
class C:

    def __init__(self):
        pass

    def add(self):
        print('in C')

class D(C):

    def show(self):
        print('in D')

    def func(self):
        self.show()
obj = D()
obj.show()  # 通过对象访问
obj.func()  # 类内部访问
obj.add()  # 派生类中访问



# 2.2.6 私有方法
class C:

    def __init__(self):
        pass

    def __add(self):
        print('in C')

class D(C):

    def __show(self):
        print('in D')

    def func(self):
        self.__show()# 这是允许的，因为是在类内部调用私有方法
obj = D()
# obj.__show()  # 通过不能对象访问
obj.func()  # 类内部可以访问
# obj.__add()  # 派生类中不能访问





# 2.2 类的其他成员

'''
这里的其他成员主要就是类方法：

方法包括：普通方法、静态方法和类方法，三种方法在内存中都归属于类，区别在于调用方式不同。

实例方法

  定义：第一个参数必须是实例对象，该参数名一般约定为“self”，通过它来传递实例的属性和方法（也可以传类的属性和方法）；
  调用：只能由实例对象调用。

类方法

  定义：使用装饰器@classmethod。第一个参数必须是当前类对象，该参数名一般约定为“cls”，通过它来传递类的属性和方法（不能传实例的属性和方法）；
  调用：实例对象和类对象都可以调用。

静态方法

  定义：使用装饰器@staticmethod。参数随意，没有“self”和“cls”参数，但是方法体中不能使用类或实例的任何属性和方法；
  调用：实例对象和类对象都可以调用。

双下方法（后面会讲到）

　定义：双下方法是特殊方法，他是解释器提供的 由爽下划线加方法名加爽下划线 方法名的具有特殊意义的方法,双下方法主要是python源码程序员使用的，
　　　　我们在开发中尽量不要使用双下方法，但是深入研究双下方法，更有益于我们阅读源码。

　调用：不同的双下方法有不同的触发方式，就好比盗墓时触发的机关一样，不知不觉就触发了双下方法，例如：init


'''


print("""\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n""")


# 2.2.1 类方法
'''
使用装饰器@classmethod。

原则上，类方法是将类本身作为对象进行操作的方法。假设有个方法，且这个方法在逻辑上采用类本身作为对象来调用更合理，那么这个方法就可以定义为类方法。另外，如果需要继承，也可以定义为类方法。

如下场景：

假设我有一个学生类和一个班级类，想要实现的功能为： 
执行班级人数增加的操作、获得班级的总人数； 学生类继承自班级类，每实例化一个学生，班级人数都能增加； 最后，我想定义一些学生，获得班级中的总人数。

思考：这个问题用类方法做比较合适，因为我实例化的是学生，但是如果我从学生这一个实例中获得班级总人数，在逻辑上显然是不合理的。
同时，如果想要获得班级总人数，如果生成一个班级的实例也是没有必要的
'''


class Student:
    __num = 0

    def __init__(self, name, age):
        self.name = name
        self.age = age
        Student.addNum()  # 写在__new__方法中比较合适，但是现在还没有学，暂且放到这里

    @classmethod
    def addNum(cls):
        cls.__num += 1

    @classmethod
    def getNum(cls):
        return cls.__num


a = Student('张三', 18)
b = Student('李四', 36)
c = Student('王五', 73)

print(f'看看这：{Student.getNum()}')


print("""\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n""")



# 2.3.2 静态方法
'''
使用装饰器@staticmethod

静态方法是类中的函数，不需要实例。静态方法主要是用来存放逻辑性的代码，逻辑上属于类，但是和类本身没有关系，也就是说在静态方法中，不会涉及到类中的属性和方法的操作。
可以理解为，静态方法是个独立的、单纯的函数，它仅仅托管于某个类的名称空间中，便于使用和维护。

譬如，我想定义一个关于时间操作的类，其中有一个获取当前时间的函数。

    如下，使用了静态方法（函数），然而方法体中并没使用（也不能使用）类或实例的属性（或方法）。若要获得当前时间的字符串时，并不一定需要实例化对象，此时对于静态方法而言，所在类更像是一种名称空间。

    其实，我们也可以在类外面写一个同样的函数来做这些事，但是这样做就打乱了逻辑关系，也会导致以后代码维护困难。
'''

import time

class TimeTest(object):
    def __init__(self, hour, minute, second):
        self.hour = hour
        self.minute = minute
        self.second = second

    @staticmethod
    def showTime():
        return time.strftime("%H:%M:%S", time.localtime())


print(TimeTest.showTime())
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime)






print("""\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n""")

# 2.3.3 属性
'''

什么是特性property

property是一种特殊的属性，访问它时会执行一段功能（函数）然后返回值


【为什么要用property】

将一个类的函数定义成特性以后，对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的，这种特性的使用方式【遵循了统一访问的原则】

例一：BMI指数（bmi是计算而来的，但很明显它听起来像是一个属性而非方法，如果我们将其做成一个属性，更便于理解）

    成人的BMI数值：
    过轻：低于18.5
    正常：18.5-23.9
    过重：24-27
    肥胖：28-32
    非常肥胖, 高于32
    　　体质指数（BMI）=体重（kg）÷身高^2（m）
    　　EX：70kg÷（1.75×1.75）=22.86


'''


class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)


p1 = People('meet', 100, 1.85)
print(p1.bmi)

print("""\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n""")


# 由于新式类中具有三种访问方式，我们可以根据他们几个属性的访问特点，分别将三个方法定义为对同一个属性：获取、修改、删除

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self, value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')


# 只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1 = Foo()
f1.AAA
f1.AAA = 'aaa'
del f1.AAA


# 或者：
class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self, value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')

    AAA = property(get_AAA, set_AAA, delete_AAA)  # 内置property三个参数与get,set,delete一一对应


f1 = Foo()
f1.AAA
f1.AAA = 'aaa'
del f1.AAA

print("""\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n""")




# 看一个例子：商品示例


class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter #只有set的时候才需要传一个参数，get和del只需要一个self
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
del obj.price     # 删除商品原价




print("""\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n""")




# 2.3. isinstace 与 issubclass

"""
1. `isinstance()`
   - 用途: 用于检查一个对象是否是一个类的实例，或者是一个子类的实例。
   - 适用场景: 确认对象是否具有某些属性或方法，处理多态。
   - 语法: isinstance(object, classinfo)
     - `object`: 要检查的对象。
     - `classinfo`: 可以是一个类，也可以是多个类组成的元组。如果`classinfo`是元组，则只要`object`是元组中任一类的实例，就返回`True`。
   - 示例:
     ```python
     class Fruit:
         pass

     class Apple(Fruit):
         pass

     apple = Apple()
     print(isinstance(apple, Apple))  # 输出: True
     print(isinstance(apple, Fruit))  # 输出: True
     print(isinstance(apple, (list, tuple, Apple)))  # 输出: True
     print(isinstance(apple, (list, tuple)))  # 输出: False
     ```

2. `issubclass()`
   - 用途: 用于检查一个类是否是另一个类的子类。
   - 适用场景: 设计类层次结构，实现基于类的API。
   - 语法: issubclass(class, classinfo)
     - `class`: 要检查的类。
     - `classinfo`: 可以是一个类，也可以是多个类组成的元组。如果`classinfo`是元组，则只要`class`是元组中任一类的子类，就返回`True`。
   - 示例:
     ```python
     class Fruit:
         pass

     class Apple(Fruit):
         pass

     class Banana(Fruit):
         pass

     print(issubclass(Apple, Fruit))  # 输出: True
     print(issubclass(Apple, Apple))  # 输出: True (每个类都是其自身的子类)
     print(issubclass(Apple, Banana))  # 输出: False
     print(issublass(Apple, (Fruit, Banana)))  # 输出: True
     ```

3. 总结
   - 使用`isinstance()`来检查一个对象是否属于某个类或其子类的实例。
   - 使用`issubclass()`来检查一个类是否是另一个类的子类。
   - 这两个函数都可以接受元组作为第二个参数，提供灵活的类型检查。
"""

# 思考：那么 list str tuple dict等这些类与 Iterble类 的关系是什么？

from collections import Iterable

print(isinstance([1,2,3], list))  # True
print(isinstance([1,2,3], Iterable))  # True
print(issubclass(list,Iterable))  # True

# 由上面的例子可得，这些可迭代的数据类型，list str tuple dict等 都是 Iterable的子类。








print("""\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n""")

# 下面的例子
class Human:
    """
    此类主要是构建人类
    """
    mind = '有思想'  # 第一部分：静态属性 属性 静态变量 静态字段
    language = '使用语言'

    def __init__(self, name, sex, age, hobby):
        # self 和 obj 指向的是同一个内存地址同一个空间，下面就是通过self给这个对象空间封装四个属性。
        self.n = name
        self.s = sex
        self.a = age
        self.h = hobby

    def work(self):  # 第二部分：方法 函数 动态属性
        # print(self)
        print('人类会工作')

    def tools(self):
        print('人类会使用工具')


# todo 三. 从类名的角度研究类

# 3.1 类名操作静态属性

# 第一种，查看类中的所有内容：类名.__dict__方式。

print(Human.__dict__)
# 以下了解:
print(Human.__dict__['mind'])
# Human.__dict__['mind'] = '无脑'  #这里会有错误，不能进行修改
# 通过这种方式只能查询，不能增删改.
print(Human.__dict__)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# 第二种：万能的点.
# 通过万能的点 可以增删改查类中的单个属性

print(Human.mind)  # 查
Human.mind = '无脑'  # 改
print(Human.mind)
del Human.mind  # 删
Human.walk = '直立行走'
print(Human.walk)

'''对两种做一个总结：如果想查询类中的所有内容，通过 第一种__dict__方法，如果只是操作单个属性则用万能的点的方式。'''

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# 3.2 类名操作动态方法

'''前提：除了两个特殊方法：属性，类方法之外，一般不会通过类名操作一个类中的方法。'''

Human.work(111)
Human.tools(111)
# 下面可以做，但不用。
Human.__dict__['work'](111)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# 四. 从对象的角度研究类

# 4.1 什么是对象

'''
对象是从类中出来的，只要是类名加上（），这就是一个实例化过程，这个就会实例化一个对象。

【一个类可以实例化多个对象，每实例化一个对象都开辟一个空间,并且这写空间之间是独立的】

其实实例化一个对象总共发生了三件事：

　　1，在内存中开辟了一个对象空间。

　　2，自动执行类中的__init__方法，并将这个对象空间（内存地址）传给了__init__方法的第一个位置参数self。

　　3，在__init__ 方法中通过self给对象空间添加属性。

【self】

    类中的方法一般都是通过对象执行的（除去类方法，静态方法外），并且对象执行这些方法都会自动将对象空间传给方法中的第一个参数self.

【self】其实就是类中方法（函数）的第一个位置参数，只不过解释器会自动将调用这个函数的对象传给self。
所以把类中的方法的第一个参数约定俗成设置成self, 代表这个就是对象.这个self可以进行改变但是不建议大家进行修改

【传参分为隐式传参和显示传参,self这种方式就是隐式传参】



Python：
使用类来创建对象。
使用 . 来访问对象的属性和方法。

        class Human:
            """
            此类主要是构建人类
            """
            mind = '有思想'

            def work(self):
                print('人类会工作')

        # 创建对象
        human = Human()

        # 访问对象属性
        print(human.mind)  # 输出：有思想

        # 调用对象方法
        human.work()  # 输出：人类会工作



Java：
使用 new 关键字和构造函数来创建对象。
使用 . 来访问对象的属性和方法。


        public class Human {
            /**
             * 此类主要是构建人类
             */
            public static String mind = "有思想";

            public void work() {
                System.out.println("人类会工作");
            }

            public static void main(String[] args) {
                // 创建对象
                Human human = new Human();

                // 访问对象属性
                System.out.println(human.mind);  // 输出：有思想

                // 调用对象方法
                human.work();  // 输出：人类会工作
            }
        }


Scala：
使用 new 关键字和类名来创建对象。
使用 . 来访问对象的属性和方法。
        class Human {
          var mind: String = "有思想"

          def work(): Unit = {
            println("人类会工作")
          }
        }

        object Main extends App {
          // 创建对象
          val human = new Human

          // 访问对象属性
          println(human.mind)  // 输出：有思想

          // 调用对象方法
          human.work()  // 输出：人类会工作
        }



'''

#  对象的使用


# 实例化对象
obj = Human('金星', '男', 18, '运动')

# 对象操作属性
'''
Java和Scala必须在类中进行添加和删除，Python可以动态添加和删除比较灵活

属性的添加
    Java: 需要在类中添加新的私有属性，并添加相应的getter和setter方法。这种方式较为冗长，但能明确控制属性的访问权限。
    Scala: 直接在类中添加新的var或val属性。这种方式较为简洁，但属性的可变性需要注意。
    Python: 可以在类定义外部动态添加属性。这种方式非常灵活，适合快速开发，但也可能导致代码难以维护。
属性的删除
    Java: 需要手动删除属性声明及其相关的getter和setter方法。这种方式较为繁琐，但能确保代码的完整性。
    Scala: 需要手动删除属性声明。这种方式较为简洁，但仍需修改类的源码。
    Python: 可以使用del关键字动态删除属性。这种方式非常灵活，但需要小心避免属性的误删除
    
属性声明的地方：

Java: 属性在类内部声明为私有，并通过公共的getter和setter方法进行访问和修改。声明位置通常在类的顶部。

    public class Person {
        private String name;  // 属性声明
        private int age;      // 属性声明
    
        // 构造方法
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // getter 和 setter 方法
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }


Scala: 属性在类的构造参数中声明，使用var或val关键字。声明位置在类定义的参数列表中。 （我一般类似Java那种，这里不细说，而且用的都是比object创建，不用class）

    class Person(var name: String, var age: Int) {  // 属性声明
    
        // 方法
        def shout(): Unit = {
            println("Shouting!")
        }
    }


Python: 属性在类的构造方法__init__中声明，也可以在类定义外部动态添加。声明位置通常在__init__方法中。

    class Person:
        def __init__(self, name, age):  # 属性声明
            self.name = name
            self.age = age
    
        # 方法
        def shout(self):
            print("Shouting!")
    
    # 动态添加属性
    person = Person("John", 30)
    person.address = "123 Main St"  # 动态添加属性

'''

# ①使用对象.__dict__

print(obj.__dict__)


# ②使用万能的点：对象.

obj.job = '舞蹈家'  # 增
del obj.n  # 删
obj.s = '女'  # 改
print(obj.s)  # 查
print(obj.__dict__)

# 对象查看类中的属性

print(obj.language)
obj.a = 20
print(obj.a)

# 对象操作类中的方法

obj.work()
obj.tools()
